package com.njtoyo.taxi.admin.service.data.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import cn.hutool.poi.excel.StyleSet;
import com.alibaba.fastjson.JSON;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njtoyo.taxi.admin.handler.FilterHandler;
import com.njtoyo.taxi.admin.library.CommUtil;
import com.njtoyo.taxi.admin.library.Const;
import com.njtoyo.taxi.admin.library.common.Enum;
import com.njtoyo.taxi.admin.mapper.master.mapper.DriverMapper;
import com.njtoyo.taxi.admin.mapper.master.mapper.DriverServiceLogMapper;
import com.njtoyo.taxi.admin.mapper.master.mapper.data.DriverDataMapper;
import com.njtoyo.taxi.admin.rest.export.driver.DriverAppraiseData;
import com.njtoyo.taxi.admin.rest.presenter.data.chart.RankChartItemPresenter;
import com.njtoyo.taxi.admin.rest.presenter.data.data_analysis.DriverDataPresenter;
import com.njtoyo.taxi.admin.rest.presenter.platfrom.AdminUserIdentityPresenter;
import com.njtoyo.taxi.admin.rest.wrapper.data.data_analysis.ChartWrapper;
import com.njtoyo.taxi.admin.rest.wrapper.data.data_analysis.DriverDataWrapper;
import com.njtoyo.taxi.admin.service.data.DriverServiceLogService;
import com.njtoyo.taxi.entity.backend.AdminUser;
import com.taxi.entity.common.RestResult;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.apache.poi.ss.usermodel.CellStyle;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
@DS(Const.PRIMARY_DS)
@RequiredArgsConstructor
public class DriverServiceLogServiceImpl implements DriverServiceLogService {
    private final DriverServiceLogMapper driverServiceLogMapper;
    private final DriverMapper driverMapper;
    private final DriverDataMapper driverDataMapper;

    @Override
    public RestResult<List<RankChartItemPresenter>> workingTaxiChart(AdminUser adminUser, ChartWrapper wrapper) {
        List<RankChartItemPresenter> res = new ArrayList<>();
        FilterHandler filterHandler = new FilterHandler(adminUser, wrapper.getCantonId(), wrapper.getCompanyId());
        DateTime startDate = DateTime.of(wrapper.getStartTime());
        DateTime endDate = DateTime.of(DateUtil.endOfDay(wrapper.getEndTime()));
        if (wrapper.getGroupType().equals(Enum.DateGroupType.month)) {
            startDate = DateUtil.beginOfMonth(wrapper.getStartTime());
            endDate = DateUtil.endOfMonth(wrapper.getEndTime());
        }
        List<Map> working = new ArrayList();
        switch (filterHandler.getFilterType()) {
            case platform:
                working = driverServiceLogMapper.getWorkingTaxiGroupByDate(null, null, startDate, endDate, wrapper.getGroupType().name());
                break;
            case canton:
                working = driverServiceLogMapper.getWorkingTaxiGroupByDate(filterHandler.getCantonIds(), null, startDate, endDate, wrapper.getGroupType().name());
                break;
            case company:
                working = driverServiceLogMapper.getWorkingTaxiGroupByDate(null, filterHandler.getCompanyIds(), startDate, endDate, wrapper.getGroupType().name());
                break;
        }
        Map<Object, Object> result = working.stream().collect(Collectors.toMap(i -> i.get("statsDate"), i -> i.get("taxiCount")));
        Date currentDate = startDate;
        if (wrapper.getGroupType().equals(Enum.DateGroupType.month)) {
            while (currentDate.before(endDate)) {
                RankChartItemPresenter item = new RankChartItemPresenter();
                item.setStart(DateTime.of(currentDate));

                long count = (long) result.getOrDefault(DateUtil.format(currentDate, DatePattern.NORM_MONTH_PATTERN), 0L);
                // 翻倍率
                Double rate = filterHandler.getFakeMultiple().getActiveTaxi();
                if (Objects.nonNull(rate)) {
                    count = Math.round(Long.parseLong(Long.toString(count)) * rate);
                }
                item.setTotal(count);
                res.add(item);
                currentDate = DateUtil.offsetMonth(currentDate, 1);
            }
        } else {
            while (currentDate.before(endDate)) {
                RankChartItemPresenter item = new RankChartItemPresenter();
                item.setStart(DateTime.of(currentDate));

                long count = (long) result.getOrDefault(DateUtil.format(currentDate, DatePattern.NORM_DATE_PATTERN), 0L);
                // 翻倍率
                Double rate = filterHandler.getFakeMultiple().getActiveTaxi();
                if (Objects.nonNull(rate)) {
                    count = Math.round(Long.parseLong(Long.toString(count)) * rate);
                }
                item.setTotal(count);
                res.add(item);
                currentDate = DateUtil.offsetDay(currentDate, 1);
            }
        }

        return RestResult.success(res);
    }

    @Override
    public RestResult<List<RankChartItemPresenter>> workingDriverChart(AdminUser adminUser, ChartWrapper wrapper) {
        List<RankChartItemPresenter> res = new ArrayList<>();
        FilterHandler filterHandler = new FilterHandler(adminUser, wrapper.getCantonId(), wrapper.getCompanyId());
        DateTime startDate = DateTime.of(wrapper.getStartTime());
        DateTime endDate = DateTime.of(DateUtil.endOfDay(wrapper.getEndTime()));
        if (wrapper.getGroupType().equals(Enum.DateGroupType.month)) {
            startDate = DateUtil.beginOfMonth(wrapper.getStartTime());
            endDate = DateUtil.endOfMonth(wrapper.getEndTime());
        }
        List<Map> working = new ArrayList();
        switch (filterHandler.getFilterType()) {
            case platform:
                working = driverServiceLogMapper.getWorkingTaxiGroupByDate(null, null, startDate, endDate, wrapper.getGroupType().name());
                break;
            case canton:
                working = driverServiceLogMapper.getWorkingTaxiGroupByDate(filterHandler.getCantonIds(), null, startDate, endDate, wrapper.getGroupType().name());
                break;
            case company:
                working = driverServiceLogMapper.getWorkingTaxiGroupByDate(null, filterHandler.getCompanyIds(), startDate, endDate, wrapper.getGroupType().name());
                break;
        }
        Map<Object, Object> result = working.stream().collect(Collectors.toMap(i -> i.get("statsDate"), i -> i.get("driverCount")));
        Date currentDate = startDate;
        if (wrapper.getGroupType().equals(Enum.DateGroupType.month)) {
            while (currentDate.before(endDate)) {
                RankChartItemPresenter item = new RankChartItemPresenter();
                item.setStart(DateTime.of(currentDate));

                long count = (long) result.getOrDefault(DateUtil.format(currentDate, DatePattern.NORM_MONTH_PATTERN), 0L);
                // 翻倍率
                Double rate = filterHandler.getFakeMultiple().getActiveDriver();
                if (Objects.nonNull(rate)) {
                    count = Math.round(Long.parseLong(Long.toString(count)) * rate);
                }
                item.setTotal(count);
                res.add(item);
                currentDate = DateUtil.offsetMonth(currentDate, 1);
            }
        } else {
            while (currentDate.before(endDate)) {
                RankChartItemPresenter item = new RankChartItemPresenter();
                item.setStart(DateTime.of(currentDate));

                long count = (long) result.getOrDefault(DateUtil.format(currentDate, DatePattern.NORM_DATE_PATTERN), 0L);
                // 翻倍率
                Double rate = filterHandler.getFakeMultiple().getActiveDriver();
                if (Objects.nonNull(rate)) {
                    count = Math.round(Long.parseLong(Long.toString(count)) * rate);
                }
                item.setTotal(count);
                res.add(item);
                currentDate = DateUtil.offsetDay(currentDate, 1);
            }
        }

        return RestResult.success(res);
    }

    @Override
    public RestResult<?> driverData(AdminUser adminUser, DriverDataWrapper wrapper) {
        AdminUserIdentityPresenter identity = (AdminUserIdentityPresenter) adminUser.getIdentity();
        switch (adminUser.getAccountType()) {
            case canton:
                wrapper.setCantonIds(identity.getCantonIds());
                break;
            case company:
                wrapper.setCompanyIds(identity.getCompanyIds());
        }
        Page<DriverDataPresenter> res = driverMapper.getDriverDateList(wrapper);

        long total = res.getTotal();
        if (total > 0) {
            List<Long> driverIds = res.getRecords().stream().map(DriverDataPresenter::getDriverId).collect(Collectors.toList());

            wrapper.setDriverIds(driverIds);
            List<DriverDataPresenter> incomeList = driverDataMapper.getIncome(wrapper.getDriverIds(), wrapper.getStartDate(), wrapper.getEndDate()); // income
            List<DriverDataPresenter> callNumList = driverDataMapper.getCallNum(wrapper.getDriverIds(), wrapper.getStartDate(), wrapper.getEndDate());// callNum
            List<DriverDataPresenter> endNumList = driverDataMapper.getEndNum(wrapper.getDriverIds(), wrapper.getStartDate(), wrapper.getEndDate());// endNum
            List<DriverDataPresenter> driverCancelList = driverDataMapper.getDriverCancel(wrapper.getDriverIds(), wrapper.getStartDate(), wrapper.getEndDate());// driverCancel
            List<DriverDataPresenter> customerCancelList = driverDataMapper.getCustomerCancel(wrapper.getDriverIds(), wrapper.getStartDate(), wrapper.getEndDate());// customerCancel
            List<DriverDataPresenter> fiveMakeList = driverDataMapper.getFiveMake(wrapper.getDriverIds(), wrapper.getStartDate(), wrapper.getEndDate());// fiveMake
            List<DriverDataPresenter> recommendList = driverDataMapper.getRecommend(wrapper.getDriverIds(), wrapper.getStartDate(), wrapper.getEndDate());// recommend
            List<DriverDataPresenter> confirmCallList = driverDataMapper.getConfirmCall(wrapper.getDriverIds(), wrapper.getStartDate(), wrapper.getEndDate());// driver_confirm_call_persisted

            int i = (int) ((wrapper.getCurrent() - 1) * wrapper.getSize() + 1);
            for (DriverDataPresenter record : res.getRecords()) {
                Long driverId = record.getDriverId();
                record.setNo(i++);
                record.setIncome(CommUtil.listToResult(incomeList, driverId, DriverDataPresenter::getIncome, BigDecimal.ZERO));
                record.setCallNum(CommUtil.listToResult(callNumList, driverId, DriverDataPresenter::getCallNum, 0));
                record.setEndNum(CommUtil.listToResult(endNumList, driverId, DriverDataPresenter::getEndNum, 0));
                record.setEndRate(CommUtil.div(record.getEndNum(), record.getCallNum()));
                record.setEndRateStr(CommUtil.rateStr(record.getEndRate()));
                record.setDriverCancel(CommUtil.listToResult(driverCancelList, driverId, DriverDataPresenter::getDriverCancel, 0));
                record.setCustomerCancel(CommUtil.listToResult(customerCancelList, driverId, DriverDataPresenter::getCustomerCancel, 0));
                record.setFiveMake(CommUtil.listToResult(fiveMakeList, driverId, DriverDataPresenter::getFiveMake, 0));
                record.setRecommend(CommUtil.listToResult(recommendList, driverId, DriverDataPresenter::getRecommend, 0));
                record.setConfirmCall(CommUtil.listToResult(confirmCallList, driverId, DriverDataPresenter::getConfirmCall, 0));
            }
            res.setTotal(total);
        }
        return RestResult.success(res);
    }

    @SneakyThrows
    @Override
    public void driverDataExport(AdminUser adminUser, DriverDataWrapper wrapper, HttpServletResponse response) {
        AdminUserIdentityPresenter identity = (AdminUserIdentityPresenter) adminUser.getIdentity();
        switch (adminUser.getAccountType()) {
            case canton:
                wrapper.setCantonIds(identity.getCantonIds());
                break;
            case company:
                wrapper.setCompanyIds(identity.getCompanyIds());
        }
        wrapper.setCurrent(1L);
        wrapper.setSize(Integer.MAX_VALUE);
        Page<DriverDataPresenter> res = driverMapper.getDriverDateList(wrapper);
        List<DriverAppraiseData> rows = new ArrayList<>();
        if (CollUtil.isNotEmpty(res.getRecords())) {
            List<Long> driverIds = res.getRecords().stream().map(DriverDataPresenter::getDriverId).collect(Collectors.toList());
            wrapper.setDriverIds(driverIds);
            List<DriverDataPresenter> incomeList = driverDataMapper.getIncome(wrapper.getDriverIds(), wrapper.getStartDate(), wrapper.getEndDate()); // income
            List<DriverDataPresenter> callNumList = driverDataMapper.getCallNum(wrapper.getDriverIds(), wrapper.getStartDate(), wrapper.getEndDate());// callNum
            List<DriverDataPresenter> endNumList = driverDataMapper.getEndNum(wrapper.getDriverIds(), wrapper.getStartDate(), wrapper.getEndDate());// endNum
            List<DriverDataPresenter> driverCancelList = driverDataMapper.getDriverCancel(wrapper.getDriverIds(), wrapper.getStartDate(), wrapper.getEndDate());// driverCancel
            List<DriverDataPresenter> customerCancelList = driverDataMapper.getCustomerCancel(wrapper.getDriverIds(), wrapper.getStartDate(), wrapper.getEndDate());// customerCancel
            List<DriverDataPresenter> fiveMakeList = driverDataMapper.getFiveMake(wrapper.getDriverIds(), wrapper.getStartDate(), wrapper.getEndDate());// fiveMake
            List<DriverDataPresenter> recommendList = driverDataMapper.getRecommend(wrapper.getDriverIds(), wrapper.getStartDate(), wrapper.getEndDate());// recommend
            List<DriverDataPresenter> confirmCallList = driverDataMapper.getConfirmCall(wrapper.getDriverIds(), wrapper.getStartDate(), wrapper.getEndDate());// driver_confirm_call_persisted

            int i = (int) ((wrapper.getCurrent() - 1) * wrapper.getSize() + 1);
            for (DriverDataPresenter record : res.getRecords()) {
                DriverAppraiseData row = new DriverAppraiseData();
                Long driverId = record.getDriverId();
                row.setDriverName(record.getDriverName());
                row.setPlateNumber(record.getPlateNumber());
                row.setCantonName(record.getCantonName());
                row.setCompanyName(record.getCompanyName());
                row.setMarkScore(record.getMarkScore());
                row.setIncome(CommUtil.listToResult(incomeList, driverId, DriverDataPresenter::getIncome, BigDecimal.ZERO));
                row.setCallNum(CommUtil.listToResult(callNumList, driverId, DriverDataPresenter::getCallNum, 0));
                row.setEndNum(CommUtil.listToResult(endNumList, driverId, DriverDataPresenter::getEndNum, 0));
                row.setEndRateStr(CommUtil.rateStr(CommUtil.div(row.getEndNum(), row.getCallNum())));
                row.setDriverCancel(CommUtil.listToResult(driverCancelList, driverId, DriverDataPresenter::getDriverCancel, 0));
                row.setCustomerCancel(CommUtil.listToResult(customerCancelList, driverId, DriverDataPresenter::getCustomerCancel, 0));
                row.setFiveMake(CommUtil.listToResult(fiveMakeList, driverId, DriverDataPresenter::getFiveMake, 0));
                row.setRecommend(CommUtil.listToResult(recommendList, driverId, DriverDataPresenter::getRecommend, 0));
                row.setConfirmCall(CommUtil.listToResult(confirmCallList, driverId, DriverDataPresenter::getConfirmCall, 0));

                rows.add(row);
            }
            ExcelWriter writer = ExcelUtil.getWriter();
            StyleSet style = writer.getStyleSet();
            CellStyle cellStyle = style.getHeadCellStyle();
            cellStyle.setWrapText(true);
            //自定义标题别名
            writer.addHeaderAlias("driverName", "司机名称");
            writer.addHeaderAlias("plateNumber", "车牌号");
            writer.addHeaderAlias("cantonName", "城市");
            writer.addHeaderAlias("companyName", "公司");
            writer.addHeaderAlias("markScore", "阳光分");
            writer.addHeaderAlias("income", "收入");
            writer.addHeaderAlias("confirmCall", "听单量");
            writer.addHeaderAlias("callNum", "应答量");
            writer.addHeaderAlias("endNum", "完单量");
            writer.addHeaderAlias("endRateStr", "完单率");
            writer.addHeaderAlias("driverCancel", "应答后司机取消量");
            writer.addHeaderAlias("customerCancel", "应答后乘客取消量");
            writer.addHeaderAlias("fiveMake", "五星好评");
            writer.addHeaderAlias("recommend", "推广数");
            writer.write(rows, true);
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=cars.xls");
            ServletOutputStream out = response.getOutputStream();

            writer.flush(out, true);
            writer.close();
            IoUtil.close(out);
        }
    }
}
